package main

import (
	"errors"
	"github.com/gin-gonic/gin"
	"io"
	"log"
	"os"
	"strconv"
	"strings"
)

func DownloadFile(c *gin.Context) {
	filePath := "path/to/file"
	fileName := "file_name"
	file, err := os.Open(filePath)
	if err != nil {
		c.AbortWithError(404, err)
		return
	}
	defer file.Close()
	stat, err := file.Stat()
	if err != nil {
		c.AbortWithError(404, err)
		return
	}
	c.Writer.Header().Set("Content-Disposition", "attachment; filename="+fileName)
	c.Writer.Header().Set("Content-Type", "application/octet-stream")
	c.Writer.Header().Set("Content-Length", strconv.FormatInt(stat.Size(), 10))
	c.Writer.Flush()
	var offset int64 = 0
	var bufsize int64 = 1024 * 1024 // 1MB
	buf := make([]byte, bufsize)
	rangeHeader := c.Request.Header.Get("Range")
	if rangeHeader != "" {
		parts := strings.Split(rangeHeader, "=")
		if len(parts) == 2 && parts[0] == "bytes" {
			rangeStr := parts[1]
			ranges := strings.Split(rangeStr, "-")
			if len(ranges) == 2 {
				offset, _ = strconv.ParseInt(ranges[0], 10, 64)
				if offset >= stat.Size() {
					c.AbortWithError(416, errors.New("Requested Range Not Satisfiable"))
					return
				}
				if ranges[1] != "" {
					endOffset, _ := strconv.ParseInt(ranges[1], 10, 64)
					if endOffset >= stat.Size() {
						endOffset = stat.Size() - 1
					}
					c.Writer.Header().Set("Content-Range", "bytes "+ranges[0]+"-"+strconv.FormatInt(endOffset, 10)+"/"+strconv.FormatInt(stat.Size(), 10))
					c.Writer.Header().Set("Content-Length", strconv.FormatInt(endOffset-offset+1, 10))
					file.Seek(offset, 0)
				} else {
					c.Writer.Header().Set("Content-Range", "bytes "+ranges[0]+"-"+strconv.FormatInt(stat.Size()-1, 10)+"/"+strconv.FormatInt(stat.Size(), 10))
					c.Writer.Header().Set("Content-Length", strconv.FormatInt(stat.Size()-offset, 10))
					file.Seek(offset, 0)
				}
				c.Writer.WriteHeader(206)
			}
		}
	}
	for {
		n, err := file.ReadAt(buf, offset)
		if err != nil && err != io.EOF {
			log.Println("read file error", err)
			break
		}
		if n == 0 {
			break
		}
		_, err = c.Writer.Write(buf[:n])
		if err != nil {
			log.Println("write file error", err)
			break
		}
		c.Writer.Flush()
		offset += int64(n)
	}
	c.Writer.Flush()
}
